package com.imaker.frame.ssl

import android.content.Context
import android.util.Log
import com.aliyun.sls.android.producer.*
import com.imaker.frame.network.RetrofitApi
import com.imaker.frame.utils.NetWorkUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

/**
 * @Description: 阿里云ssl日志埋点
 * @author ziheng
 * @date 2023/3/25 14:16
 */
class SslLog {

    companion object {
        val instance: SslLog by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            SslLog()
        }
    }

    private val TAG = "SslLog"

    private var endpoint = "https://cn-beijing.log.aliyuncs.com"
    private var project = "vese-test"
    private var logstore = "vese-logstore"
    private var accesskeyid = ""
    private var accesskeysecret = ""
    private var securityToken = ""
    private var client: LogProducerClient? = null
    private var logProducerConfig: LogProducerConfig? = null

    private var mContext: Context? = null

    //是否正在请求token
    private var isLoadToken = false


    fun initSslLog(context: Context) {
        mContext = context
        if (accesskeyid == "" || accesskeysecret == "" || securityToken == "") {
            if (!NetWorkUtil.isNetworkConnected(mContext!!)) return
            Log.i(TAG, "initSslLog: 请求了token接口")
            reqeustToken()
        } else {
            createClient()
        }
    }


    /**
     * 请求获取token接口
     */
    private fun reqeustToken() {
        GlobalScope.launch {
            isLoadToken = true
            val resp = RetrofitApi.service.getSslToken()
            if (resp.code == 200) {
                accesskeyid = resp.data?.accessKeyId.toString()
                accesskeysecret = resp.data?.accessKeySecret.toString()
                securityToken = resp.data?.securityToken.toString()
                if(client == null){
                    try {
                        Log.i(TAG, "initSslLog: $accesskeyid  $accesskeysecret  $securityToken")
                        createClient()
                        isLoadToken = false
                    } catch (e: LogProducerException) {
                        e.printStackTrace()
                        Log.e(TAG, "reqeustToken: 发生了错误")
                    }
                }else{
                    isLoadToken = false
                    logProducerConfig?.resetSecurityToken(accesskeyid,accesskeysecret,securityToken)
                }
            }
        }
    }

    /**
     * 发送延迟埋点
     * @param flush 1及时发送 0延迟发送
     */
    fun sendLog(log: com.aliyun.sls.android.producer.Log,flush: Int = 0) {
        Log.e(TAG, "sendLog start: ${log.content}")
        val res = client?.addLog(log,flush)
        Log.e(TAG, "sendLog end: $res 、 ${res?.isLogProducerResultOk}")
    }


    fun oneLog(): com.aliyun.sls.android.producer.Log {
        val log = Log()
        log.putContent("channel", "debug渠道")
        return log
    }


    @Throws(LogProducerException::class)
    private fun createClient() {
        // 指定sts token 创建config，过期之前调用resetSecurityToken重置token
        logProducerConfig = LogProducerConfig(
            mContext, endpoint, project, logstore, accesskeyid, accesskeysecret,securityToken
        )
        // 设置主题
        logProducerConfig!!.setTopic("test_topic")
        // 设置tag信息，此tag会附加在每条日志上
        logProducerConfig!!.addTag("test", "test_tag")
        // 每个缓存的日志包的大小上限，取值为1~5242880，单位为字节。默认为1024 * 1024
        logProducerConfig!!.setPacketLogBytes(1024 * 1024)
        // 每个缓存的日志包中包含日志数量的最大值，取值为1~4096，默认为1024
        logProducerConfig!!.setPacketLogCount(1024)
        // 被缓存日志的发送超时时间，如果缓存超时，则会被立即发送，单位为毫秒，默认为3000
        logProducerConfig!!.setPacketTimeout(3000)
        // 单个Producer Client实例可以使用的内存的上限，超出缓存时add_log接口会立即返回失败
        // 默认为64 * 1024 * 1024
        logProducerConfig!!.setMaxBufferLimit(64 * 1024 * 1024)
        // 发送线程数，默认为1
        logProducerConfig!!.setSendThreadCount(1)

        // 1 开启断点续传功能， 0 关闭
        // 每次发送前会把日志保存到本地的binlog文件，只有发送成功才会删除，保证日志上传At Least Once
        logProducerConfig!!.setPersistent(0)
        // 持久化的文件名，需要保证文件所在的文件夹已创建。配置多个客户端时，不应设置相同文件
        logProducerConfig!!.setPersistentFilePath(mContext?.filesDir.toString() + "/log.dat")
        // 是否每次AddLog强制刷新，高可靠性场景建议打开
//        logProducerConfig!!.setPersistentForceFlush(1)
        // 持久化文件滚动个数，建议设置成10。
        logProducerConfig!!.setPersistentMaxFileCount(10)
        // 每个持久化文件的大小，建议设置成1-10M
        logProducerConfig!!.setPersistentMaxFileSize(1024 * 1024)
        // 本地最多缓存的日志数，不建议超过1M，通常设置为65536即可
        logProducerConfig!!.setPersistentMaxLogCount(65536)

        //网络连接超时时间，整数，单位秒，默认为10
        logProducerConfig!!.setConnectTimeoutSec(10)
        //日志发送超时时间，整数，单位秒，默认为15
        logProducerConfig!!.setSendTimeoutSec(10)
        //flusher线程销毁最大等待时间，整数，单位秒，默认为1
        logProducerConfig!!.setDestroyFlusherWaitSec(2)
        //sender线程池销毁最大等待时间，整数，单位秒，默认为1
        logProducerConfig!!.setDestroySenderWaitSec(2)
        //数据上传时的压缩类型，默认为LZ4压缩，0 不压缩，1 LZ4压缩，默认为1
        logProducerConfig!!.setCompressType(1)
        //设备时间与标准时间之差，值为标准时间-设备时间，一般此种情况用户客户端设备时间不同步的场景
        //整数，单位秒，默认为0；比如当前设备时间为1607064208, 标准时间为1607064308，则值设置为 1607064308 - 1607064208 = 10
        logProducerConfig!!.setNtpTimeOffset(3)
        //日志时间与本机时间之差，超过该大小后会根据 `drop_delay_log` 选项进行处理。
        //一般此种情况只会在设置persistent的情况下出现，即设备下线后，超过几天/数月启动，发送退出前未发出的日志
        //整数，单位秒，默认为7*24*3600，即7天
        logProducerConfig!!.setMaxLogDelayTime(7 * 24 * 3600)
        //对于超过 `max_log_delay_time` 日志的处理策略
        //0 不丢弃，把日志时间修改为当前时间; 1 丢弃，默认为 1 （丢弃）
        logProducerConfig!!.setDropDelayLog(0)
        //是否丢弃鉴权失败的日志，0 不丢弃，1丢弃
        //默认为 0，即不丢弃
        logProducerConfig!!.setDropUnauthorizedLog(0)
        //注册 获取服务器时间 的函数
//        config.setGetTimeUnixFunc(new LogProducerTimeUnixFunc() {
//            @Override
//            public long getTimeUnix() {
//                return System.currentTimeMillis()/1000;
//            }
//        });
        client = LogProducerClient(
            logProducerConfig
        ) { resultCode, reqId, errorMessage, logBytes, compressedBytes ->
            if(resultCode == 6 && !isLoadToken){
                Log.e(TAG, "createClient: 是false，所以请求")
                reqeustToken()
            }
            Log.e(
                TAG,
                "onCall: resultCode = $resultCode 、 reqId = $reqId 、errorMessage = $errorMessage、 logBytes = $logBytes 、compressedBytes = $compressedBytes"
            )
        }
    }


}